package cn.wcy.design_patterns.test.Controller.demo2.test2;

import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.YearMonth;
import java.util.List;
import java.util.stream.Collectors;

import static java.time.temporal.ChronoUnit.DAYS;

/**
 * 预算计划
 *
 * @author wcy
 * @date 2024/06/15
 */
@Service("BudgetPlan3")
public class BudgetPlan {

    /**
     * 查询
     *
     * 该函数根据给定的开始日期和结束日期，计算预算在这段时间内的平均每天预算。
     * 如果开始日期和结束日期的月份相同，则直接计算这个月的预算金额除以这个月的天数，再乘以开始日期和结束日期之间的天数
     * 如果开始日期和结束日期的月份不同，则需要将这段时间分为开始月份、中间月份和结束月份三部分，
     * 分别计算每部分的预算金额和天数，最后将三部分的预算金额相加。如果开始日期和结束日期无效，则抛出IllegalArgumentException异常。
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return long
     */
    public long query(LocalDate startDate, LocalDate endDate) {

        if (startDate.withDayOfMonth(1).equals(endDate.withDayOfMonth(1))) {
            long amount = getBudgetAmount(startDate);
            long daysInPeriod = getBudgetDaysCount(startDate);
            long daysBetween = startDate.until(endDate, DAYS) + 1;
            long budgetAmount = amount / daysInPeriod * daysBetween;
            return budgetAmount;
        }

        if (YearMonth.from(startDate).isBefore(YearMonth.from(endDate))) {
            long amountStartPeriod = getBudgetAmount(startDate);
            long daysInStartPeriod = getBudgetDaysCount(startDate);
            long daysAfterStartDateInStartPeriod = startDate.until(startDate.withDayOfMonth(startDate.lengthOfMonth()), DAYS) + 1;
            long totalStartPeriod = amountStartPeriod / daysInStartPeriod * daysAfterStartDateInStartPeriod;

            long totalInMiddle = 0;
            for (Budget budget : getBudgetsBetween(startDate, endDate)) {
                totalInMiddle +=budget.getAmount();
            }

            long amountEndPeriod = getBudgetAmount(endDate);
            long daysInEndPeriod = getBudgetDaysCount(endDate);
            long daysBeforeEndDateInEndPeriod = endDate.getDayOfMonth();
            long totalEndPeriod = amountEndPeriod / daysInEndPeriod * daysBeforeEndDateInEndPeriod;

            return totalStartPeriod + totalInMiddle + totalEndPeriod;
        }

        throw new IllegalArgumentException("Invalid input");
    }

    private long getBudgetDaysCount(LocalDate date) {
        Budget budget = getBudgetContaining(date);
        return budget.getMonth().lengthOfMonth();
    }

    private Budget getBudgetContaining(LocalDate date) {
        return BudgetRepo.budgets.stream()
                .filter(budget -> budget.getMonth().atDay(1).equals(date.withDayOfMonth(1)))
                .findFirst().orElse(new Budget(0L, YearMonth.from(date)));
    }

    private long getBudgetAmount(LocalDate startDate) {
        Budget budgetContaining = getBudgetContaining(startDate);
        return budgetContaining.getAmount();
    }

    private List<Budget> getBudgetsBetween(LocalDate startDate, LocalDate endDate) {
        return BudgetRepo.budgets.stream()
                .filter(budget -> budget.getMonth().atDay(1).isAfter(startDate) && budget.getMonth().atEndOfMonth().isBefore(endDate))
                .collect(Collectors.toList());
    }
}
